/*
 * Алгоритм Прима
 * vim:foldmethod=marker:
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>
#include <assert.h>

#define N_MAX	100

struct adjlist {
	struct Node	*first;
	struct Node	*last;
	int		size;
	uint8_t		used;
	long long	d; //distance from 's' to this vertex
	int		prev;
};

struct Node {
	int		x;
	long long	weight;
	int		index;
	char		is_queued;
	struct Node	*next;
};

struct edge {
	int		a, b;
	long long	weight;
	int		index;
};

struct prio_queue {
	struct edge	*array;
	int		size;
	int		size_max;
};

struct mst {
	int		*edges;
	long long	tree_weight;
	int		n, size;
};

/*****************************************************************************/

#define SWAP_INT(a, b)	(((a) ^ (b)) && ((b) ^= (a) ^= (b), (a) ^= (b)))

// {{{ adjlist interface functions
static void
pushback(struct adjlist *al, int x, int weight, int idx)
{
	struct Node *newnode;
	static const char *prefix = "pushback():";

	newnode = (struct Node *) calloc(1UL, sizeof (struct Node));
	//fprintf(stderr, "%s newnode=%p", prefix, (void *) newnode);
	newnode->x         = x;
	newnode->weight    = weight;
	newnode->next      = NULL;
	newnode->is_queued = 0;
	newnode->index     = idx;
	//fprintf(stderr, "\n\t->x=%d\n\t->weight=%lld\n\t->index=%d\n",
	//		newnode->x, newnode->weight, newnode->index);
	if (!al->first) {
	//	fprintf(stderr, "%s assigning newnode=%p to al->first=%p\n",
	//			prefix, (void *) newnode, (void *) al->first);
		al->first = newnode;
	} else {
	//	fprintf(stderr, "%s assigning newnode=%p to al->last->next\n",
	//			prefix, (void *) newnode);
		al->last->next = newnode;
	}
	al->last = newnode;
	al->size++;
}

static void
free_al(struct adjlist *al)
{
	struct Node *n, *p;
	p = n = al->first;
	while (n) {
		n = n->next;
		free(p);
		p = n;
	}
}
// }}}

// {{{ priority queue
static void
pq_destroy(struct prio_queue *pq)
{
	if (pq->size_max) free(pq->array);
}

static void
Heap__sift_down(struct prio_queue *pq, int start)
{
	int parent = start,
	    child,
	    swap;

	while (parent * 2 + 1 < pq->size) {
		child = parent * 2 + 1;
		swap = parent;

		if (pq->array[parent].weight > pq->array[child].weight) {
			swap = child;
			#ifdef DEBUG
			fprintf(stderr, "Heap__sift_down(): parent=%d child=%d, swap=%d ; pq->array[parent].weight=%d pq->array[child].weight=%d\n",
					parent, child, swap, pq->array[parent].weight, pq->array[child].weight);
			#endif
		}

		if (child+1 < pq->size &&
		    pq->array[swap].weight > pq->array[child+1].weight)
			swap = child+1;

		if (swap != parent) {
			SWAP_INT(pq->array[parent].a, pq->array[swap].a);
			SWAP_INT(pq->array[parent].b, pq->array[swap].b);
			SWAP_INT(pq->array[parent].weight, pq->array[swap].weight);
			SWAP_INT(pq->array[parent].index, pq->array[swap].index);
			parent = swap;
		} else {
			return;
		}
	}
}

static void
Heap__sift_up(struct prio_queue *pq, int start, int end)
{
	register int child = end, parent = (end - 1) / 2;

	/*
	 * 'start' is a kind of left barrier for this function
	 */
        while (child > start)
		if (pq->array[(child - 1) / 2].weight > pq->array[child].weight) {
			SWAP_INT(pq->array[parent].a, pq->array[child].a);
			SWAP_INT(pq->array[parent].b, pq->array[child].b);
			SWAP_INT(pq->array[parent].weight, pq->array[child].weight);
			SWAP_INT(pq->array[parent].index, pq->array[child].index);

			child = parent;
			parent = (child - 1) / 2;
		} else {
			break;
		}
}

static void
Heap__heapify(struct prio_queue *pq)
{
	register int start = pq->size / 2 - 1;

	while (start >= 0)
		Heap__sift_down(pq, start--);
}

static struct edge
pq_extract_min(struct prio_queue *pq)
{
	SWAP_INT(pq->array[0].a, pq->array[pq->size-1].a);
	SWAP_INT(pq->array[0].b, pq->array[pq->size-1].b);
	SWAP_INT(pq->array[0].weight, pq->array[pq->size-1].weight);
	SWAP_INT(pq->array[0].index, pq->array[pq->size-1].index);
	--pq->size;
	Heap__sift_down(pq, 0);

	return pq->array[pq->size];
}

static void
pq_init(struct prio_queue *pq, const int n)
{
	pq->array = (struct edge *) calloc(8, sizeof *pq->array);
	pq->size = 0;
	pq->size_max = 8;
}

static void
pq_insert(struct prio_queue *pq, const int a_idx, const int b_idx,
		const int weight, const int index)
{
	struct edge *tmp;

	if (pq->size + 1 > pq->size_max) {
		tmp = (struct edge *) realloc(pq->array, (sizeof *tmp) * (pq->size_max *= 2));
		if (tmp == NULL) {
			fprintf(stderr, "failed to realloc pq->array (new size = %d)\n",
					pq->size_max);
			assert(tmp != NULL);
		} else
			pq->array = tmp;
	}

	pq->array[pq->size].a = a_idx;
	pq->array[pq->size].b = b_idx;
	pq->array[pq->size].weight = weight;
	pq->array[pq->size].index  = index;
	if (pq->array[pq->size].a > pq->array[pq->size].b)
		SWAP_INT(pq->array[pq->size].a, pq->array[pq->size].b);
	pq->size += 1;

	Heap__sift_up(pq, 0, pq->size - 1);
}

static unsigned char
pq_is_empty(struct prio_queue *pq)
{
	return (pq->size ? 0 : 1);
}

static void
pq_print(struct prio_queue *pq, char *delim)
{
	register int i = 0;

	for (i = 0; i < pq->size; i++)
		fprintf(stderr, "i=%5d: { a=%d b=%d weight=%lld }%s", i,
			pq->array[i].a, pq->array[i].b, pq->array[i].weight, delim);
	putchar('\n');
}
// }}}

void
mst_add_node(struct mst *T, const int edge_idx)
{
	int *tmp = NULL;

	if (T->size == 0) {
		T->edges = (int *) calloc(1UL<<4, sizeof *T->edges);
		T->size  = 8;
	}
	if (T->n + 2 >= T->size) {
		tmp = (int *) realloc(T->edges, (T->size *= 2) * sizeof *T->edges);
		if (tmp)
			T->edges = tmp;
		else {
			fprintf(stderr, " failed to realloc T->edges (new size = %d)\n", T->size);
			exit(EXIT_FAILURE);
		}
	}
	T->edges[T->n++] = edge_idx;
}

void
mst_print(struct mst *T)
{
	//int i = 0;

	printf("%lld\n", T->tree_weight);
	/* while (i < T->n)
		printf("%d\n", T->edges[i++] + 1); */
}

void
mst_destroy(struct mst *t)
{
	if (t->size) free(t->edges);
	t->size = 0;
}

int
prim(struct adjlist *al, const int n, struct prio_queue *pq, struct mst *mstree)
{
	register int i = 0;
	int u = 0;
	struct Node *node;
	struct edge e;

	for (node = al[0].first; node; node = node->next)
		pq_insert(pq, 0, node->x, node->weight, node->index);
	for (i = 0; i < n; ++i)
		al[i].d = LLONG_MAX;

	al[0].used = 1;
	mstree->tree_weight = 0;
	al[0].d = 0LL;
	al[0].prev = -1;

game_over_retry:
	while (!pq_is_empty(pq)) {
		e = pq_extract_min(pq);
		if (al[e.a].used && al[e.b].used)
			goto game_over_retry;
		u = al[e.a].used ? e.b : e.a;

		al[u].used = 1;
		mst_add_node(mstree, e.index);
		mstree->tree_weight += e.weight;

		node = al[u].first;
		while (node) {
			if (!node->is_queued && !al[node->x].used) {
				node->is_queued = 1;
				pq_insert(pq, u, node->x, node->weight, node->index);
			}
			node = node->next;
		}

		node = al[u].first;
		while (node) {
			if (!al[node->x].used && al[node->x].d > node->weight) {
				al[node->x].d = node->weight;
				al[node->x].prev = u;
			}
			node = node->next;
		}
	}

	for (i = 0; i < n; ++i)
		if (!al[i].used) {
			fprintf(stderr, "node %d { used=%d d=%lld prev=%d } is not in the MST\n",
					i + 1, al[i].used, al[i].d, al[i].prev);
			if ((node = al[i].first)) {
				while (node) {
					fprintf(stderr, " { node->x=%d node->weight=%lld node->next=%p }\n",
							node->x + 1, node->weight, node->next);
					node = node->next;
				}
			}
			mstree->n = 0;
			mstree->tree_weight = 0LL;
			return 1;
		}

	return 0;
}

int
main(void)
{
	int n, m, i = 0;
	struct adjlist *al;
	struct prio_queue pq;
	int a, b, w;
	struct mst min_spanning_tree;

	scanf("%d%d", &n, &m);

	al = (struct adjlist *) calloc((unsigned long) n, sizeof *al); //<<<
	min_spanning_tree.tree_weight = min_spanning_tree.n = min_spanning_tree.size = 0;
	pq_init(&pq, n);
	while (i < m) {
		scanf("%d%d%d", &a, &b, &w);
		--a,b--;
		pushback(al + a, b, w, i);
		pushback(al + b, a, w, i);
		i++;
	}

	prim(al, n, &pq, &min_spanning_tree);
	
	mst_print(&min_spanning_tree);

	mst_destroy(&min_spanning_tree);
	pq_destroy(&pq);
	for (i = 0; i < n; i++)
		free_al(al+i);
	free(al);
	return 0;
}
